#include <QString>
#include <QFont>
#include <math.h>
#include <QCursor>
#include <QToolTip>
#include <QImage>
#include <QFileDialog>
#include <QFile>
#include <QTextStream>

#include "graphwidget.h"
#include "dotitem.h"
#include "lineitem.h"

GraphWidget::GraphWidget(QWidget *parent) :
    QGraphicsView(parent),
    coordVisible(true),
    ctrlDown(false),
    altDown(false),
    shiftDown(false),
    pointIndex(0)
{
    scene = new QGraphicsScene(this);
    scene->setItemIndexMethod(QGraphicsScene::NoIndex);
    scene->setSceneRect(parent->rect());
    this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    qDebug() << parent->rect();
//    scene->setSceneRect(-100, -100, 400, 400);
    setScene(scene);
    setCacheMode(CacheBackground);
    setViewportUpdateMode(BoundingRectViewportUpdate);
    setRenderHint(QPainter::Antialiasing);
    setTransformationAnchor(AnchorUnderMouse);
    scale(qreal(1.0), qreal(1.0));
    setMinimumSize(1024, 768);
    setWindowTitle(tr("Coordinates Recorder"));
//    setCursor(Qt::CrossCursor);
//    coordList = new QList<CoordItem*>;

    actHideCoord = new QAction(tr("Hide Coordinates"), this);
    connect(actHideCoord, SIGNAL(triggered()), this, SLOT(setCoordVisible()));
    this->addAction(actHideCoord);
    actHideCoord->setCheckable(true);

    actActiveAddDots = new QAction(tr("Add Dots"), this);
    connect(actActiveAddDots, SIGNAL(triggered()), this, SLOT(activeAddDots()));
    this->addAction(actActiveAddDots);
    actActiveAddDots->setCheckable(true);

    actActiveAddLines = new QAction(tr("Add Lines"), this);
    connect(actActiveAddLines, SIGNAL(triggered()), this, SLOT(activeAddLines()));
    this->addAction(actActiveAddLines);
    actActiveAddLines->setCheckable(true);

    actActiveDelete = new QAction(tr("Delete"), this);
    connect(actActiveDelete, SIGNAL(triggered()), this, SLOT(activeDelete()));
    this->addAction(actActiveDelete);
    actActiveDelete->setCheckable(true);

    actClearAll = new QAction(tr("Clear All"), this);
    connect(actClearAll, SIGNAL(triggered()), this, SLOT(clearAllItems()));
    this->addAction(actClearAll);

    actSave2Image = new QAction(tr("Save to Image"), this);
    connect(actSave2Image, SIGNAL(triggered()), this, SLOT(save2Image()));
    this->addAction(actSave2Image);

    actClose = new QAction(tr("Close"), this);
    connect(actClose, SIGNAL(triggered()), this, SLOT(close()));
    this->addAction(actClose);

    this->setContextMenuPolicy(Qt::ActionsContextMenu);

//    mouseC = new CoordItem(this);
//    scene->addItem(mouseC);
//    mouseC->setPos(scene->width()-80,scene->height()-20);
}

void GraphWidget::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Escape)
        this->close();
    else if(event->key() == Qt::Key_Control) {
        ctrlDown = true;
        actActiveAddDots->setChecked(true);
    }
    else if(event->key() == Qt::Key_Alt) {
        altDown = true;
        actActiveDelete->setChecked(true);
    }
    else if(event->key() == Qt::Key_Shift) {
        shiftDown = true;
        actActiveAddLines->setChecked(true);
    } else if(event->key() == Qt::Key_Left) {
        QPoint oldPos = QCursor::pos();
        QCursor::setPos(oldPos.x()-1, oldPos.y());
    } else if(event->key() == Qt::Key_Right) {
        QPoint oldPos = QCursor::pos();
        QCursor::setPos(oldPos.x()+1, oldPos.y());
    } else if(event->key() == Qt::Key_Up) {
        QPoint oldPos = QCursor::pos();
        QCursor::setPos(oldPos.x(), oldPos.y()-1);
    } else if(event->key() == Qt::Key_Down) {
        QPoint oldPos = QCursor::pos();
        QCursor::setPos(oldPos.x(), oldPos.y()+1);
    } else if(event->key() == Qt::Key_P) {
        mouseLeftClicked();
    }
}

void GraphWidget::keyReleaseEvent(QKeyEvent *event)
{
    //TODO
    if (event->key() == Qt::Key_Control) {
        ctrlDown = false;
        actActiveAddDots->setChecked(false);
    } else if (event->key() == Qt::Key_Alt) {
        altDown = false;
        actActiveDelete->setChecked(false);
    } else if(event->key() == Qt::Key_Shift) {
        shiftDown = false;
        actActiveAddLines->setChecked(false);
    }
}

void GraphWidget::mouseLeftClicked()
{
    //Update the cursor positon
    QPoint newPos = QCursor::pos();
//        mouseC->setNewCoord(newCoord);
    QString tip = QString("(%1,%2)").arg(newPos.x()).arg(newPos.y());
    QToolTip::showText(newPos, tip);
    this->viewport()->update();

    if (true == ctrlDown) {
        //Add a dot
        CoordItem *newCoord = new CoordItem(this);
        newCoord->setNewCoord(newPos);
        DotItem *newDot = new DotItem(this);
//        coordList->append(newCoord);
//        newCoord->setParentItem(newDot);

        scene->addItem(newDot);
        newDot->setPos(newPos);
        scene->addItem(newCoord);
        newCoord->setPos(newPos.x()+10,newPos.y());
    }

    if (true == altDown){
        //Delet Items
        if (QGraphicsItem *item = itemAt(QCursor::pos())) {
             if (item->type() == DOTTYPE) {
                if (QGraphicsItem *item = itemAt(QCursor::pos())) {
                     if (item->type() == DOTTYPE) {
                         QPoint point(QCursor::pos().x()+10, QCursor::pos().y()+2);
                         if (QGraphicsItem* text = itemAt(point))
                             if (text->type() == TEXTTYPE) {
                                scene->removeItem(text);
//                                coordList->removeOne(text);
                             }
                     }
                     scene->removeItem(item);
                 }
             }
        }
    }

    if (true == shiftDown){
        //Connect Line
        if (0 == pointIndex) {
            lastPoint = newPos;
            pointIndex +=1;
        } else if (pointIndex = 1) {
            LineItem *newLine = new LineItem(this, lastPoint, newPos);
            scene->addItem(newLine);
            qreal left = lastPoint.x() < newPos.x() ? lastPoint.x(): newPos.x();
            qreal top = lastPoint.y() < newPos.y() ? lastPoint.y(): newPos.y();
            newLine->setPos(0, 0);
#ifdef DEBUG
            qDebug() << "Line position:" << left << top;
#endif
            pointIndex = 0;
        }
    }
}

void GraphWidget::mousePressEvent(QMouseEvent *event)
{
    //TODO
    if (event->button() == Qt::LeftButton){
#ifdef DEBUG
//        qDebug() << "Left button pressed!";
#endif
        mouseLeftClicked();
    }
    if (event->button() == Qt::RightButton) {
        //Right button menu
#ifdef DEBUG
//        qDebug() << "Right button pressed!";
#endif
    }
}

void GraphWidget::drawBackground(QPainter *painter, const QRectF &rect)
{
    //TODO
    //Fill
    QRectF sceneRect = this->sceneRect();
    painter->fillRect(rect.intersect(sceneRect), Qt::white);
    painter->setBrush(Qt::NoBrush);

    //Grid
    int step = 50;
    int constY = this->height();
    int constX = this->width();
    painter->setPen(Qt::gray);
    for (int i=step; i<constX;) {
        painter->drawLine(i, 0, i, constY);
        i += step;
    }
    for (int i=step; i<constY;) {
        painter->drawLine(0, i, constX, i);
        i += step;
    }

    //Text
    QFont font = painter->font();
    font.setPointSize(FONTSIZE);
    painter->setFont(font);
    painter->setPen(Qt::gray);
    QRectF textRect(sceneRect.right()-220, sceneRect.bottom()-50,
                    210, 50);
    QString message(tr("Hold Ctrl to add dots, hold Alt to remove dos, hold Shift to connect Dots with line"));;
    painter->drawText(textRect, message);
}

void GraphWidget::setCoordVisible()
{
    QList <QGraphicsItem*> itemList = scene->items();
#ifdef DEBUG
    qDebug() << "Total items:" << itemList.count();
#endif
    if (itemList.count() > 0) {
        for (int i=0; i<itemList.count(); i++) {
            QGraphicsItem *item = itemList.at(i);
            if (item->type() == TEXTTYPE) {
    #ifdef DEBUG
                qDebug() << "Item type:" << item->type();
    #endif
                if (coordVisible) {
                    item->setVisible(false);
                } else {
                    item->setVisible(true);
                }
            }
        }
        actHideCoord->setChecked(coordVisible);
        coordVisible = !coordVisible;
    }
}

void GraphWidget::clearAllItems()
{
    scene->clear();
//    coordList->clear();
}

void GraphWidget::activeAddDots()
{
    if (ctrlDown) {
        actActiveAddDots->setChecked(false);
        ctrlDown = false;
    } else {
        actActiveAddDots->setChecked(true);
        ctrlDown = true;
    }
}

void GraphWidget::activeAddLines()
{
    if (shiftDown) {
        actActiveAddLines->setChecked(false);
        shiftDown = false;
    } else {
        actActiveAddLines->setChecked(true);
        shiftDown = true;
    }
}

void GraphWidget::activeDelete()
{
    if (altDown) {
        actActiveDelete->setChecked(false);
        altDown = false;
    } else {
        actActiveDelete->setChecked(true);
        altDown = true;
    }
}

void GraphWidget::save2Image()
{
    QImage image(this->rect().size(), QImage::Format_ARGB32);
    QPainter painter(&image);
    scene->render(&painter);
    QString imgPath = QFileDialog::getSaveFileName(this,
                                                tr("Choose a filename to save under"),
                                                "/",
                                                "Images (*.png)");
    image.save(imgPath);

    QString txtPath = QFileDialog::getSaveFileName(this,
                                                tr("Choose a filename to save under"),
                                                "/",
                                                "Text File (*.txt)");
    QFile newFile(txtPath);
    QList <QGraphicsItem*> itemList = scene->items();
#ifdef DEBUG
    qDebug() << "Total items:" << itemList.count();
#endif
    if (itemList.count() > 0 && newFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        for (int i=0; i<itemList.count(); i++) {
            QGraphicsItem *item = itemList.at(i);
            if (item->type() == TEXTTYPE) {
    #ifdef DEBUG
                qDebug() << "Item type:" << item->type();
    #endif
                CoordItem *coord = static_cast <CoordItem*>(item);
                QTextStream out(&newFile);
                out << coord->getText() << "\n";
            }
        }
    }
    newFile.close();
}
